package hypeerweb;

import java.util.Iterator;
import java.util.Set;

public class DeleteCommand extends Command
{

    @Override
    public boolean isExtreme(Node currNode)
    {
        //elegant
        return currNode.getWebId().getValue() == 0;

    }

    @Override
    public Node goExtreme(Node currNode)
    {
        Node smallestNeighbor = Node.NULL_NODE;
        Set<Node> set = currNode.getNeighbors();
        Iterator<Node> it = set.iterator();
        if (isExtreme(currNode))
        {
            return currNode;
        }
        else
        {
            if (it.hasNext())
            {
                smallestNeighbor = it.next();
            }
            while (it.hasNext())
            {
                Node tempNode = (Node) it.next();

                if (tempNode.getWebId().getValue() < smallestNeighbor
                        .getWebId().getValue())
                {
                    smallestNeighbor = tempNode;
                }

            }
         
            if (smallestNeighbor != Node.NULL_NODE)
            {
                return smallestNeighbor;
            }
            else
            {
                return currNode;
            }
        }
    }

    @Override
    //checked looks good
    public boolean isOpPoint(Node currNode)
    {
    
        Node largestNeighbor = Node.NULL_NODE;
        Set<Node> set = currNode.getNeighbors();
        Iterator<Node> it = set.iterator();
        if (it.hasNext())
        {
            largestNeighbor = it.next();
        }
        while (it.hasNext())
        {
            Node tempNode = (Node) it.next();

            if (tempNode.getWebId().getValue() > largestNeighbor.getWebId()
                    .getValue())
            {
                largestNeighbor = tempNode;
            }

        }
        if (largestNeighbor == Node.NULL_NODE
                || (largestNeighbor.getWebId().getValue() < currNode.getWebId()
                        .getValue()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    @Override
    public Node goOpPoint(Node currNode)
    {
        Node largestNeighbor = null;
        Set<Node> set = currNode.getNeighbors();

        Iterator<Node> it = set.iterator();
        if (isOpPoint(currNode))
        {
            return currNode;
        }
        else
        {
            if (it.hasNext())
            {
                largestNeighbor = it.next();
            }
            while (it.hasNext())
            {
                Node tempNode = (Node) it.next();

                if (tempNode.getWebId().getValue() > largestNeighbor.getWebId()
                        .getValue())
                {
                    largestNeighbor = tempNode;
                }

            }
              assert (largestNeighbor != Node.NULL_NODE);
            //looks like there will be an assertion if there is only one node
            //when something is deleted or one beyond the cap node 
            return largestNeighbor;
        }
    }

    @Override
    public void connect(Node opNode, Node opPoint)
    {
        
        disconnect(opPoint);
        replace(opNode,opPoint);
        
    }

    private void disconnect(Node opPoint)
    {
        assert(opPoint.getUpPointers().isEmpty());
        Node smallest = Node.NULL_NODE;
        for (Node n: opPoint.getNeighbors())
        {
            if(smallest == Node.NULL_NODE || n.getWebId().getValue() < smallest.getWebId().getValue())
            {
                smallest = n;
            }
        }
        smallest.decrementHeight();
        opPoint.removeNeighbor(smallest);
        smallest.removeNeighbor(opPoint);
        smallest.setState(opPoint.getParentState());
        for (Node n: opPoint.getNeighbors())
        {
            smallest.addUpPointer(n);
            n.addDownPointer(smallest);
            n.removeNeighbor(opPoint);
            n.updateState();
        }
        for (Node n: opPoint.getDownPointers())
        {
            n.removeUpPointer(opPoint);
        }
        if(opPoint.getWebId().getValue() == 1)
        {
            smallest.setFold(smallest);
            smallest.setInverseSurrogateFold(Node.NULL_NODE);
            smallest.setSurrogateFold(Node.NULL_NODE);
        }
        else if (smallest.getSurrogateFold() != Node.NULL_NODE)
        {
            smallest.setFold(opPoint.getFold()); // I get you as my fold
            smallest.setInverseSurrogateFold(Node.NULL_NODE);
            smallest.setSurrogateFold(Node.NULL_NODE);
            smallest.getFold().setFold(smallest); //I tell you I am now your fold.
            smallest.getFold().setInverseSurrogateFold(Node.NULL_NODE);
            smallest.getFold().setSurrogateFold(Node.NULL_NODE);
        }
        else
        {
            smallest.setInverseSurrogateFold(opPoint.getFold());
            smallest.getInverseSurrogateFold().setSurrogateFold(smallest);
            smallest.getInverseSurrogateFold().setFold(Node.NULL_NODE);
        }
    }

    private void replace(Node opNode,Node opPoint)
    {
        //there might be an issue if the opNode is the opPoint
        opPoint.clearAll();
        if(opNode == Node.NULL_NODE)
        {
            assert(false);
            return;
        }
        opPoint.setWebId(opNode.getWebId());
        opPoint.setState(opNode.getState());
        //folds
        opPoint.setFold(opNode.getFold());
        if(opPoint.getFold() != Node.NULL_NODE)
        {
            opPoint.getFold().setFold(opPoint);
        }
        opPoint.setSurrogateFold(opNode.getSurrogateFold());
        if(opPoint.getSurrogateFold() != Node.NULL_NODE)
        {
            opPoint.getSurrogateFold().setInverseSurrogateFold(opPoint);
        }
        opPoint.setInverseSurrogateFold(opNode.getInverseSurrogateFold());
        if(opPoint.getInverseSurrogateFold() != Node.NULL_NODE)
        {
            opPoint.getInverseSurrogateFold().setSurrogateFold(opPoint);
        }
        //neighbors
        for(Node n : opNode.getNeighbors())
        {
            opPoint.addNeighbor(n);
            n.replaceNeighbor(opNode,opPoint);
        }
        for(Node n : opNode.getUpPointers())
        {
            opPoint.addUpPointer(n);
            n.replaceDownPointer(opNode,opPoint);
        }
        for(Node n : opNode.getDownPointers())
        {
            opPoint.addDownPointer(n);
            n.replaceUpPointer(opNode,opPoint);
        }
  
    }
    
    

}
